unit Unit1;

interface

uses Windows, Messages, SysUtils, Forms, TntForms, Bass, ShellApi, IniFiles, options,
     Classes, WinTypes, Menus, ComObj, ExtCtrls, StdCtrls, Controls,
     Graphics, TntGraphics, pldb, ComCtrls, Buttons, TntStdCtrls, TntSysUtils,
     TntDialogs, TntGrids, dll, functions, Grids, Dialogs, ShlObj,
     TntClipBrd, TntSystem, header, Unit4, plman, InfoF, About, TntMenus;

type
  TTrackBar_ = class(TTrackBar); // Override TTrackBar class

  TForm1 = class(TTntForm)
    Label4: TLabel;
    Timer1: TTimer;
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    meniu: TMenuItem;
    Playlist2: TMenuItem;
    Help1: TMenuItem;
    About2: TMenuItem;
    ools2: TMenuItem;
    agEditor1: TMenuItem;
    Deletefromdisk2: TMenuItem;
    Playonstart2: TMenuItem;
    Playonadd2: TMenuItem;
    Loop2: TMenuItem;
    N3: TMenuItem;
    Stayontop2: TMenuItem;
    Addtoplaylist2: TMenuItem;
    N5: TMenuItem;
    Removeselected2: TMenuItem;
    Clearplaylist2: TMenuItem;
    N6: TMenuItem;
    Loadplaylist2: TTntMenuItem;
    Saveplaylist2: TMenuItem;
    TrackBar1: TTrackBar;
    HTML1: TMenuItem;
    PLS1: TMenuItem;
    M3U1: TMenuItem;
    XT1: TMenuItem;
    Repeat1: TMenuItem;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    SpeedButton3: TSpeedButton;
    SpeedButton4: TSpeedButton;
    SpeedButton5: TSpeedButton;
    SpeedButton6: TSpeedButton;
    TntLabel1: TTntLabel;
    TntOpenDialog1: TTntOpenDialog;
    TntStringGrid1: TTntStringGrid;
    ProgressBar1: TProgressBar;
    Options1: TMenuItem;
    Size1: TMenuItem;
    FitWidth1: TMenuItem;
    Small1: TMenuItem;
    N1: TMenuItem;
    TntSaveDialog1: TTntSaveDialog;
    Info1: TMenuItem;
    N2: TMenuItem;
    Addfolder1: TMenuItem;
    OpenContainingFolder1: TMenuItem;
    oClipboard1: TMenuItem;
    Filename1: TMenuItem;
    rack1: TMenuItem;
    Artist1: TMenuItem;
    itle1: TMenuItem;
    Album1: TMenuItem;
    Open1: TTntMenuItem;
    N4: TMenuItem;
    Mini1: TMenuItem;
    SpeedButton7: TSpeedButton;
    procedure TrackBar1Enter(Sender: TObject);
    procedure TntStringGrid1MouseWheelUp(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
    procedure TntStringGrid1MouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
    procedure TntStringGrid1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure ProgressBar1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure ProgressBar1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure TntStringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean);
    procedure TntStringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
    procedure TntStringGrid1DblClick(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure ButtonsClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure TntFormClose(Sender: TObject; var Action: TCloseAction);
    procedure Timer1Timer(Sender: TObject);
    procedure meniuClick(Sender: TObject);
    procedure meniuOpen(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure TntFormCanResize(Sender: TObject; var NewWidth, NewHeight: Integer; var Resize: Boolean);
    procedure TntLabel1DblClick(Sender: TObject);
  private
    function DoSave(sTitle, sFilter, sExtension: string; Desktop: Boolean): boolean;
    function DoOpen(oTitle, oFilter, oInDir: string; oOptions: TOpenOptions): boolean;
    procedure AddFolder(sFolder: WideString);
    procedure Mute;
    procedure WMDROPFILES(var Message: TWMDROPFILES); message WM_DROPFILES;
    procedure Trackbar1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure SavedListsOnClick(Sender: TObject);
    procedure GenerateSavedLists(PopUp: TMenuItem; Path: WideString; var SL: TSavedLists);
  public
    CurrentStream: HStream;
    TrackIndex:    Word;
    NextIndex,
    PrevIndex:     Integer;
    CS:            TState;
    PDB:           TDataBase;
    SavedLists:    TSavedLists;
    procedure DoTop;
    procedure PlayStream(Name: WideString);
    procedure PlaySong(action: TPlayType);
    procedure Scroll;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{-------- Create --------------------------------------------------------------}
procedure TForm1.FormCreate(Sender: TObject);
var
  Ini: TIniFile;
begin
  CS.CurrentDir := ExtractFilePath(ParamStr(0));

  Ini := TIniFile.Create(CS.CurrentDir + 'config.ini');
  try
    Top            := Ini.ReadInteger('General', 'Top', Screen.Height div 2 - Height div 2);
    Left           := Ini.ReadInteger('General', 'Left', Screen.Width div 2 - Width div 2);
    Width          := Ini.ReadInteger('General', 'Width', Constraints.MinWidth);
    Height         := Ini.ReadInteger('General', 'Height', 350);
    CS.istop       := Ini.ReadBool('General', 'OnTop', false);
    CS.OpenDir     := Ini.ReadString('General', 'OpenDir', GetRegStringValue('Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders', 'My Music', HKEY_CURRENT_USER));
    CS.SaveDir     := Ini.ReadString('General', 'SaveDir', GetRegStringValue('Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders', 'My Music', HKEY_CURRENT_USER));
    CS.ListDir     := Ini.ReadString('General', 'PlayListDir', GetRegStringValue('Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders', 'My Music', HKEY_CURRENT_USER));
    CS.SaveListDir := Ini.ReadString('General', 'SavedPlayListDir', CS.CurrentDir + 'SavedLists');

    CS.volume     := Ini.ReadInteger('Sound', 'Volume', 100);
    CS.Bass_float := Ini.ReadBool('Sound', 'Float', False);

    CS.loop       := Ini.ReadBool('Playback', 'Loop', false);
    CS.repeats    := Ini.ReadBool('Playback', 'Repeat', true);
    CS.playonload := Ini.ReadBool('Playback', 'Playonstart', false);
    CS.playonadd  := Ini.ReadBool('Playback', 'Playonadd', false);
    CS.LastActive := Ini.ReadInteger('Playback', 'LastActive', 0);

    CS.Skin.listbgcolor1     := Ini.ReadInteger('Skin', 'BgColor1', RGB(255, 255, 255));
    CS.Skin.listbgcolor2     := Ini.ReadInteger('Skin', 'BgColor2', RGB(244, 240, 236));
    CS.Skin.listactivecolor  := Ini.ReadInteger('Skin', 'ActiveColor', RGB(200, 120, 5));
    CS.Skin.listfontcolor    := Ini.ReadInteger('Skin', 'FontColor', RGB(0, 0, 0));
    CS.Skin.listselfontcolor := Ini.ReadInteger('Skin', 'SelectedFontColor', RGB(255, 255, 255));
    CS.Skin.listselbgcolor   := Ini.ReadInteger('Skin', 'SelectedBgColor', RGB(64, 64, 64));
    CS.Skin.UseDefaultFont   := Ini.ReadBool('Skin', 'UseDefaultFont', true);
  finally
    Ini.Free;
  end;

  if CS.Skin.UseDefaultFont then
    begin
      if not IsFontLoaded(DEFAULT_FONT) then
        LoadFont(PChar(CS.CurrentDir + 'DejaVuSansCondensed.ttf'));

      Font.Name := DEFAULT_FONT;
    end;

  LoadFont(PChar(CS.CurrentDir + 'DejaVuSans.ttf'));

  SpeedButton1.Glyph.Handle := GetBitmap('PLAY');
  SpeedButton2.Glyph.Handle := GetBitmap('BACKWARD');
  SpeedButton3.Glyph.Handle := GetBitmap('STOP');
  SpeedButton4.Glyph.Handle := GetBitmap('FORWARD');
  SpeedButton5.Glyph.Handle := GetBitmap('OPEN');
  SpeedButton6.Glyph.Handle := GetBitmap('DELETE');
  SpeedButton7.Glyph.Handle := GetBitmap('INFO');
 
  if not StartBass(Handle, CS.Bass_float) then
    Exit;

  BASS_PluginLoad(PChar(CS.CurrentDir + 'bass_mpc.dll'), 0);
  BASS_PluginLoad(PChar(CS.CurrentDir + 'bass_flac.dll'), 0);
  BASS_PluginLoad(PChar(CS.CurrentDir + 'bass_wma.dll'), 0);
  BASS_PluginLoad(PChar(CS.CurrentDir + 'bass_aac.dll'), 0);
  BASS_PluginLoad(PChar(CS.CurrentDir + 'bass_alac.dll'), 0);

  DragAcceptFiles(Handle, TRUE);

  PDB := TDataBase.Create(Form1, TntStringGrid1);

  if ParamCount > 0 then
    PDB.ParamDBList
  else
    PDB.MakeDBList;

  PDB.LoadDBList;

  If CS.istop then
    DoTop;

  TrackIndex := 0;
  NextIndex := -1;
  PrevIndex := -1;

  CS.autonext := false;
  CS.BeforeMute := 100;
  
  TrackBar1.Position := CS.volume;
  TTrackBar_(TrackBar1).OnMouseDown := Trackbar1MouseDown; // Assign an event

  Randomize; // For loop

  if PDB.GetTrackCount > CS.LastActive then
    begin
      PDB.Select(CS.LastActive);

      if CS.playonload then
        PlaySong(ptplay);
    end;
    
  if WideParamCount > 0 then
    PlaySong(ptplay);

  DoubleBuffered := true;
  TntStringGrid1.DoubleBuffered := true;
  ProgressBar1.DoubleBuffered := true;
end;

procedure TForm1.TntFormCanResize(Sender: TObject; var NewWidth, NewHeight: Integer; var Resize: Boolean);
begin
  TntStringGrid1.Height := ClientHeight - 69;
end;

procedure TForm1.TntFormClose(Sender: TObject; var Action: TCloseAction);
var
  Ini: TIniFile;
begin
  Ini := TIniFile.Create(CS.CurrentDir + 'config.ini');
  try
    Ini.WriteInteger('General', 'Top', Top);
    Ini.WriteInteger('General', 'Left', Left);
    Ini.WriteInteger('General', 'Width', Width);
    Ini.WriteInteger('General', 'Height', Height);
    Ini.WriteBool('General', 'OnTop', CS.istop);
    Ini.WriteString('General', 'OpenDir', CS.OpenDir);
    Ini.WriteString('General', 'SaveDir', CS.SaveDir);
    Ini.WriteString('General', 'PlayListDir', CS.ListDir);
    Ini.WriteString('General', 'SavedPlayListDir', CS.SaveListDir);

    Ini.WriteInteger('Sound', 'Volume', CS.volume);
    Ini.WriteBool('Sound', 'Float', CS.Bass_float);

    Ini.WriteBool('Playback', 'Repeat', CS.repeats);
    Ini.WriteBool('Playback', 'Playonstart', CS.playonload);
    Ini.WriteBool('Playback', 'Playonadd', CS.playonadd);
    Ini.WriteBool('Playback', 'Loop', CS.loop);
    Ini.WriteInteger('Playback', 'LastActive', TrackIndex);

    Ini.WriteInteger('Skin', 'BgColor1', CS.Skin.listbgcolor1);
    Ini.WriteInteger('Skin', 'BgColor2', CS.Skin.listbgcolor2);
    Ini.WriteInteger('Skin', 'ActiveColor', CS.Skin.listactivecolor);
    Ini.WriteInteger('Skin', 'FontColor', CS.Skin.listfontcolor);
    Ini.WriteInteger('Skin', 'SelectedFontColor', CS.Skin.listselfontcolor);
    Ini.WriteInteger('Skin', 'SelectedBgColor', CS.Skin.listselbgcolor);
    Ini.WriteBool('Skin', 'UseDefaultFont', CS.Skin.UseDefaultFont);
  finally
    Ini.Free;
  end;

  if IsFontLoaded(DEFAULT_FONT) then
    UnLoadFont(PChar(CS.CurrentDir + 'DejaVuSansCondensed.ttf'));
  if IsFontLoaded(DEFAULT_FONT_2) then
    UnLoadFont(PChar(CS.CurrentDir + 'DejaVuSans.ttf'));

  BASS_Free;
  BASS_PluginFree(0);
  
  PDB.SaveDBList;
  PDB.Close;
  PDB.Free;
end;

procedure TForm1.TntLabel1DblClick(Sender: TObject);
begin
  if ((BASS_ChannelIsActive(CurrentStream) = BASS_ACTIVE_PAUSED) or
     (BASS_ChannelIsActive(CurrentStream) = BASS_ACTIVE_PLAYING))
  then
    ShellExecuteW(Handle, 'explore', PWideChar(WideExtractFilePath(PDB.GetFileName(PDB.Row))), nil, nil, SW_SHOWNORMAL);
end;

{-------- Play ----------------------------------------------------------------}
procedure TForm1.PlayStream(Name: WideString);
var
  flags: Cardinal;
begin

  if CurrentStream > 0 then
    BASS_StreamFree(CurrentStream);

  if not WideFileExists(Name) then
    begin
      PDB.SetNotExists(TrackIndex);
      Dec(PDB.RetryCount);

      if PDB.RetryCount > 0 then
        PlaySong(ptnext)
      else
      begin
        TrackIndex := 0;
        PDB.RetryCount := PDB.GetTrackCount;
        Exit;
      end;
    end
  else
  begin
    if not PDB.GetExists(TrackIndex) then
      PDB.SetExists(TrackIndex);

    flags := BASS_MP3_SETPOS or
             BASS_STREAM_AUTOFREE or
             BASS_UNICODE;

    if CS.Bass_float then
      flags := flags or BASS_SAMPLE_FLOAT;

    CurrentStream := BASS_StreamCreateFile(FALSE, PWideChar(Name), 0, 0, flags);

    if CurrentStream > 0 then
      begin
        BASS_ChannelPreBuf(CurrentStream, 500);
        BASS_ChannelSetAttributes(CurrentStream, - 1, CS.volume, - 101);
        BASS_ChannelPlay(CurrentStream, FALSE);
{$R-}
        ProgressBar1.Max := BASS_ChannelGetLength(CurrentStream);
{$R+}
        ProgressBar1.Enabled := true;

        CS.FSize := GetSize(CurrentStream);
        CS.bitrate := GetBitRate(CurrentStream);
        PDB.SetTime(TrackIndex, TotalSeconds(CurrentStream));
      end;

    CS.autonext := true;

    Caption := PDB.GetFormatedTrack(TrackIndex) + '  ~~~  ';

    Label4.Caption := FormatTime(CurrentStream);
    TntLabel1.Caption := WideExtractFileName(PDB.GetFileName(TrackIndex));
    TntLabel1.Hint    := PDB.GetFileName(TrackIndex);

    Scroll;
    TntStringGrid1.Invalidate;
  end;
end;

{-------- PlaySong ------------------------------------------------------------}
procedure TForm1.PlaySong(action: TPlayType);
begin
  case Ord(action) of

    //Play
    0: begin
         if BASS_ChannelIsActive(CurrentStream) = BASS_ACTIVE_PAUSED then
           BASS_ChannelPlay(CurrentStream, False)
         else
           if PDB.GetTrackCount > 0 then
             begin
               TrackIndex := PDB.Row;
               PlayStream(PDB.GetFileName(TrackIndex));
             end;
       end;

    //Next
    1: begin 
         if PDB.GetTrackCount > 0 then
           if CS.loop then
             begin
               if PDB.GetTrackCount = 1 then
                 PlaySong(ptfirst)
               else
                 begin
                   TrackIndex := Loop(TrackIndex, PDB.GetTrackCount);
                   PlayStream(PDB.GetFileName(TrackIndex));
                 end
             end
           else
             begin
               if NextIndex > -1 then
                 begin
                   TrackIndex := NextIndex;
                   NextIndex := -1;
                   PlayStream(PDB.GetFileName(TrackIndex));
                   Exit;
                 end;

               if TrackIndex < PDB.GetTrackCount - 1 then
                 begin
                   Inc(TrackIndex);
      	           PlayStream(PDB.GetFileName(TrackIndex));
                 end
               else
                 begin
                   TrackIndex := 0;
                   PlayStream(PDB.GetFileName(TrackIndex));
                 end;
             end;
         end;

    //Previous
    2: begin
         if PDB.GetTrackCount > 0 then
           begin
             if PrevIndex > -1 then
               begin
                 TrackIndex := PrevIndex;
                 PrevIndex := -1;
                 PlayStream(PDB.GetFileName(TrackIndex));
                 Exit;
               end;

           if (TrackIndex > 0) and (TrackIndex < PDB.GetTrackCount) then
             begin
               Dec(TrackIndex);
               PlayStream(PDB.GetFileName(TrackIndex));
             end
           else
             begin
               TrackIndex := PDB.GetTrackCount - 1;
               PlayStream(PDB.GetFileName(TrackIndex));
             end;
           end;
       end;

    //Pause
    3: BASS_ChannelPause(CurrentStream);

    //Stop
    4: begin
         BASS_ChannelStop(CurrentStream);
         BASS_StreamFree(CurrentStream);
         CS.autonext := false;
       end;

    //Play first
    5: begin
         BASS_ChannelStop(CurrentStream);

         if PDB.GetTrackCount > 0 then
           begin
             TrackIndex := 0;
             PlayStream(PDB.GetFileName(TrackIndex));
           end;
       end;
  end;
end;

{-------- Channel state -------------------------------------------------------}
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  case BASS_ChannelIsActive(CurrentStream) of

    BASS_ACTIVE_STOPPED: begin
                           ProgressBar1.Position := 0;
                           ProgressBar1.Enabled  := false;
                           Caption               := '~~ SimplyPlay ~~';
                           TntApplication.Title  := Caption;
                           Label4.Caption        := '0:00/0:00';
                           TntLabel1.Caption     := '';
                           TntLabel1.Hint        := '';

                           if PDB.GetTrackCount = 0 then
                             CS.autonext := false;

                           if CS.autonext then
                             if (TrackIndex + 1 = PDB.GetTrackCount) and (CS.loop = false) then
                               begin
                                 if CS.repeats then
                                   PlaySong(ptfirst)
                                 else
                                   CS.autonext := false;
                               end
                             else
                               PlaySong(ptnext);
                         end;

    BASS_ACTIVE_PLAYING: begin
                           Label4.Caption := FormatTime(CurrentStream);
                           ProgressBar1.Position := BASS_ChannelGetPosition(CurrentStream);
                           Caption := LabelRun(Caption);
                           TntApplication.Title := Caption;
                         end;
  end;
end;

{-------- Main Controls -------------------------------------------------------}
procedure TForm1.ButtonsClick(Sender: TObject);
begin
  case TSpeedButton(Sender).Tag of
    1: PlaySong(ptprevious);
    2: meniuClick(Addtoplaylist2);
    3: if BASS_ChannelIsActive(CurrentStream) = BASS_ACTIVE_PLAYING then
         PlaySong(ptpause)
       else
         PlaySong(ptplay);
    4: PlaySong(ptstop);
    5: PlaySong(ptnext);
    6: meniuClick(Clearplaylist2);
    7: if ((BASS_ChannelIsActive(CurrentStream) = BASS_ACTIVE_PAUSED) or
          (BASS_ChannelIsActive(CurrentStream) = BASS_ACTIVE_PLAYING))
       then
         meniuClick(Info1);
  end;
end;

{-------- DragDrop ------------------------------------------------------------}
procedure TForm1.WMDROPFILES(var Message: TWMDROPFILES);
var
  I, Files: SmallInt;
  Buffer: array[0..MAX_PATH] of WideChar;
  ext: WideString;
begin
  Files := DragQueryFileW(Message.Drop, $FFFFFFFF, nil, 0);

  for I := 0 to Files - 1 do
    begin
      DragQueryFileW(Message.Drop, I, @Buffer, SizeOf(Buffer));

      if WideDirectoryExists(Buffer) then
        AddFolder(Buffer)
      else
        begin
          ext := WideExtractFileExt(WideLowerCase(Buffer));

          if (ext = '.mp3') or
             (ext = '.ogg') or
             (ext = '.wav') or
             (ext = '.mpc') or
             (ext = '.wma') or
             (ext = '.flac') or
             (ext = '.aac') or
             (ext = '.mp4') or
             (ext = '.m4a') or
             (ext = '.mod') then
            PDB.AddList(Buffer);

          if ext = '.pls' then
            PDB.ImportPL(Buffer);
        end;
    end;

  PDB.LoadDBList;

  DragFinish(Message.Drop);

  if CS.playonadd and (BASS_ChannelIsActive(CurrentStream) = BASS_ACTIVE_STOPPED) then
    PlaySong(ptplay);
end;

{-------- Menu Controls -------------------------------------------------------}
procedure TForm1.meniuClick(Sender: TObject);
var
  i: integer;
begin
  case TMainMenu(Sender).Tag of
     2: begin
          Info := TInfo.Create(Application);
          Info.Show;
        end;
     3: ShellExecuteW(Handle, 'explore', PWideChar(WideExtractFilePath(PDB.GetFileName(PDB.Row))), nil, nil, SW_SHOWNORMAL);
     4: TntClipboard.AsWideText := PDB.GetFileName(PDB.Row);
     5: TntClipboard.AsWideText := PDB.GetFormatedTrack(PDB.Row);
     6: TntClipboard.AsWideText := PDB.GetArtist(PDB.Row);
     7: TntClipboard.AsWideText := PDB.GetTrack(PDB.Row);
     8: TntClipboard.AsWideText := PDB.GetAlbum(PDB.Row);
    11: Close;
    12: begin
          Application.CreateForm(TSettings, Settings);
          try
            Settings.ShowModal;
          finally
            Settings.Free;
          end;
        end;
    13: CS.playonload := not CS.playonload;
    14: CS.playonadd := not CS.playonadd;
    15: CS.loop := not CS.loop;
    16: begin
          CS.istop := not CS.istop;
          DoTop;
        end;
    19: CS.repeats := not CS.repeats;
    21: begin
          if DoOpen('Add media', 'Playable media|*.mp3;*.wav;*.ogg;*.MOD;*.mpc;*.flac;*.wma;*.aac;*.mp4;*.m4a',
             CS.OpenDir, [ofAllowMultiSelect, ofHideReadOnly]) and
             (TntOpenDialog1.Files.Count > 0) then
            begin
              for i := 0 to TntOpenDialog1.Files.Count - 1 do
                PDB.AddList(TntOpenDialog1.Files.Strings[i]);

              PDB.LoadDBList;
              CS.OpenDir := WideExtractFileDir(TntOpenDialog1.FileName);
              if CS.playonadd and (BASS_ChannelIsActive(CurrentStream) = BASS_ACTIVE_STOPPED) then
                PlaySong(ptplay);
            end;
        end;
    22: PDB.RemoveDBList(TrackIndex, NextIndex, PrevIndex);
    23: if PDB.GetTrackCount > 0 then
          if MessageBox(Handle, 'Clear playlist?', '', MB_YESNO) = idyes then
            begin
              Caption := '~~ SimplyPlay ~~';
              TntLabel1.Caption := '';
              TrackIndex := 0;
              PDB.ClearDBList;
              PDB.ClearGrid;
            end;
    24: if DoOpen('Load playlist', '*.pls|*.pls', CS.ListDir, [ofHideReadOnly]) then
          begin
            CS.ListDir := WideExtractFileDir(TntOpenDialog1.FileName);
            PDB.ImportPL(TntOpenDialog1.FileName);
            PDB.LoadDBList;
          end;
    25: if DoSave('Export As HTML', 'HTML (*.html, *.htm)|*.html;*.htm', '.html', false) then
          begin
            CS.SaveDir := WideExtractFileDir(TntSaveDialog1.FileName);
            PDB.ExportPL(PWideChar(TntSaveDialog1.FileName), 'html');
          end;
    26: if DoSave('Export As PLS', 'PLS (*.pls)|*.pls', '.pls', false) then
          begin
            CS.ListDir := WideExtractFileDir(TntSaveDialog1.FileName);
            PDB.ExportPL(PWideChar(TntSaveDialog1.FileName), 'pls');
          end;
    27: if DoSave('Export As M3U', 'M3U (*.m3u)|*.m3u', '.m3u', false) then
          begin
            CS.ListDir := WideExtractFileDir(TntSaveDialog1.FileName);
            PDB.ExportPL(PWideChar(TntSaveDialog1.FileName), 'm3u');
          end;
    28: if DoSave('Export As TXT', 'TXT (*.txt)|*.txt', '.txt', false) then
          begin
            CS.SaveDir := WideExtractFileDir(TntSaveDialog1.FileName);
            PDB.ExportPL(PWideChar(TntSaveDialog1.FileName), 'txt');
          end;
    29: begin
          AddFolder(BrowseDialog('Select folder', BIF_RETURNONLYFSDIRS));
          PDB.LoadDBList;
        end;
    31: begin
          Application.CreateForm(TAboutBox, AboutBox);
          try
            AboutBox.ShowModal;
          finally
            AboutBox.Free;
          end;
        end;
    43: if PDB.GetTrackCount > 0 then
          if Delete_File(Handle, PWideChar(PDB.GetFileName(PDB.Row))) then
            PDB.SetNotExists(PDB.Row);
    44: PDB.FitWidth;
    45: begin
          Width := Constraints.MinWidth;
          Height := 350;
        end;
    46: begin
          Width := Constraints.MinWidth;
          Height := Constraints.MinHeight;
        end;
  end;
end;

procedure TForm1.meniuOpen(Sender: TObject);
var
  tmp: WideString;
begin
  case TMainMenu(Sender).Tag of

    //Copy To Clipboard
    101: begin
           tmp := PDB.GetFileName(PDB.Row);
           Filename1.Enabled := (Length(tmp) > 0);

           tmp := PDB.GetFormatedTrack(PDB.Row);
           itle1.Enabled := (Length(tmp) > 0);

           tmp := PDB.GetArtist(PDB.Row);
           Artist1.Enabled := (Length(tmp) > 0);

           tmp := PDB.GetTrack(PDB.Row);
           rack1.Enabled := (Length(tmp) > 0);

           tmp := PDB.GetAlbum(PDB.Row);
           Album1.Enabled := (Length(tmp) > 0);
         end;

    //Tools
    102: begin
           Deletefromdisk2.Enabled :=
               (PDB.GetTrackCount > 0) and not ((PDB.Row = TrackIndex) and
               (BASS_ChannelIsActive(CurrentStream) <> BASS_ACTIVE_STOPPED)) and
               PDB.GetExists(PDB.Row);

           OpenContainingFolder1.Enabled := (PDB.GetTrackCount > 0);
         end;

    //File
    103: begin
           Loop2.Checked        := CS.loop;
           Stayontop2.Checked   := CS.istop;
           Playonadd2.Checked   := CS.playonadd;
           Playonstart2.Checked := CS.playonload;
           Repeat1.Checked      := CS.repeats;

           Info1.Enabled := ((BASS_ChannelIsActive(CurrentStream) = BASS_ACTIVE_PAUSED) or
                            (BASS_ChannelIsActive(CurrentStream) = BASS_ACTIVE_PLAYING));
         end;

    //Playlist
    104: begin
           Removeselected2.Enabled := (PDB.GetTrackCount > 0);
           Clearplaylist2.Enabled := (PDB.GetTrackCount > 0);
         end;

    105: GenerateSavedLists(Loadplaylist2, ExtractFilePath(ParamStr(0)) + 'SavedLists', SavedLists);
  end;
end;

procedure TForm1.Mute;
begin
  if CS.volume = 0 then
    TrackBar1.Position := CS.BeforeMute
  else
    begin
      CS.BeforeMute := CS.volume;
      TrackBar1.Position := 0;
    end;
end;

{-------- Volume --------------------------------------------------------------}
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  CS.volume := TrackBar1.Position;
  BASS_ChannelSetAttributes(CurrentStream, - 1, CS.volume, - 1);
end;

procedure TForm1.TrackBar1Enter(Sender: TObject);
begin
  TntStringGrid1.SetFocus;
end;

procedure TForm1.Trackbar1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  TrackBar1.Position := Round(X * (TrackBar1.Max / TrackBar1.ClientWidth));
end;

procedure TForm1.ProgressBar1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  ProgressBar1.Position := Round(X * (ProgressBar1.Max / ProgressBar1.ClientWidth));
  BASS_ChannelSetPosition(CurrentStream, ProgressBar1.Position);
end;

procedure TForm1.ProgressBar1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  ProgressBar1.Hint := PChar(SecToTime(Round(BASS_ChannelBytes2Seconds(CurrentStream, Round(X * (ProgressBar1.Max / ProgressBar1.ClientWidth))))));
  Application.ActivateHint(Mouse.CursorPos);
end;

procedure TForm1.FormResize(Sender: TObject);
begin
  PDB.ResizeGrid;
end;

procedure TForm1.TntStringGrid1DblClick(Sender: TObject);
var
  mouserow, mousecolumn: integer;
  mpos: TPoint;
  X, Y: integer;
begin
  mpos := Mouse.CursorPos;
  X := TntStringGrid1.ScreenToClient(mpos).X;
  Y := TntStringGrid1.ScreenToClient(mpos).Y;
  TntStringGrid1.MouseToCell(X, Y, mousecolumn, mouserow);

  if (PDB.GetTrackCount > 0) and (mouserow >= 0) then
    begin
      BASS_ChannelStop(CurrentStream);
      TrackIndex := TntStringGrid1.Row;
      PlayStream(PDB.GetFileName(TrackIndex));
    end;
end;

procedure TForm1.TntStringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
begin
  with (Sender as TTntStringGrid) do
    if PDB.GetTrackCount > 0 then
      begin
        if Color <> CS.Skin.listbgcolor1 then
          Color := CS.Skin.listbgcolor1;

        begin
          // Mark missing items
          if not PDB.GetExists(ARow) then
            Canvas.Font.Style := [fsStrikeOut]
          else
            Canvas.Font.Style := [];

          // Difer line colors
          if (ARow mod 2) = 0 then
            Canvas.Brush.Color := CS.Skin.listbgcolor1
          else
            Canvas.Brush.Color := CS.Skin.listbgcolor2;

          // Handle selected items
          if (gdSelected in State) then
            begin
              Canvas.Brush.Color := CS.Skin.listselbgcolor;
              if ARow = TrackIndex then
                Canvas.Font.Color := CS.Skin.listactivecolor
              else
                Canvas.Font.Color  := CS.Skin.listselfontcolor;
            end
          else
            Canvas.Font.Color := CS.Skin.listfontcolor;

          // Mark active item
          if ARow = TrackIndex then
            Canvas.Font.Color := CS.Skin.listactivecolor;

          Canvas.Font.Name := Font.Name;
          Canvas.Font.Size := 9;
          WideCanvasTextRect(Canvas, Rect, Rect.Left + 2, Rect.Top + 1, cells[acol, arow]);
          Canvas.FrameRect(Rect);
        end;
      end
    else
      Color := clWhite;
end;

procedure TForm1.TntStringGrid1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if (Key = VK_RIGHT) or (Key = VK_LEFT) then
    Key := 0;

  case Key of
    13: TntStringGrid1DblClick(Self);
    46: if ssShift in Shift then
          PDB.ClearGrid
        else
          PDB.RemoveDBList(TrackIndex, NextIndex, PrevIndex);
    Ord('M'): Mute;
    Ord('P'): if BASS_ChannelIsActive(CurrentStream) = BASS_ACTIVE_PLAYING then
                PlaySong(ptpause)
              else
                PlaySong(ptplay);
    Ord('S'): PlaySong(ptstop);
    100: PlaySong(ptprevious);
    102: PlaySong(ptnext);
    106: TrackBar1.Position := 100;
    107: TrackBar1.Position := TrackBar1.Position + 10;
    109: TrackBar1.Position := TrackBar1.Position - 10;
  end;
end;

procedure TForm1.TntStringGrid1MouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
  Handled := true;
  if TntStringGrid1.TopRow < (TntStringGrid1.RowCount - TntStringGrid1.VisibleRowCount) then
    TntStringGrid1.TopRow := TntStringGrid1.TopRow + 1;
end;

procedure TForm1.TntStringGrid1MouseWheelUp(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
  Handled := true;
  if TntStringGrid1.TopRow > 0 then
    TntStringGrid1.TopRow := TntStringGrid1.TopRow - 1;
end;

procedure TForm1.TntStringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean);
begin
  TntStringGrid1.Invalidate;
end;

procedure TForm1.AddFolder(sFolder: WideString);
var
  ex: WideString;
  Rec: TSearchRecW;
begin
  if sFolder = '' then
    Exit;

  ex := '';
  sFolder := WideIncludeTrailingBackslash(sFolder);

  if WideFindFirst(sFolder + '*.*', faAnyFile - faDirectory, Rec) = 0 then
    try
      repeat
        ex := WideExtractFileExt(WideLowerCase(Rec.Name));

        if (ex = '.mp3') or
           (ex = '.ogg') or
           (ex = '.wav') or
           (ex = '.mpc') or
           (ex = '.flac') or
           (ex = '.wma') or
           (ex = '.aac') or
           (ex = '.mp4') or
           (ex = '.m4a') then
          PDB.AddList(sFolder + Rec.Name);

      until
        WideFindNext(Rec) <> 0;
    finally
      WideFindClose(Rec);
    end;

  if WideFindFirst(sFolder + '*', faDirectory, Rec) = 0 then
    try
      repeat
        if ((Rec.attr and faDirectory) = faDirectory) and (Rec.Name <> '.') and (Rec.Name <> '..') then
          AddFolder(sFolder + Rec.Name);
      until
        WideFindNext(Rec) <> 0;
    finally
      WideFindClose(Rec);
    end;
end;

function TForm1.DoOpen(oTitle, oFilter, oInDir: string; oOptions: TOpenOptions): boolean;
begin
  with TntOpenDialog1 do
    begin
      Title := oTitle;
      Filter := oFilter;
      FileName := '';
      InitialDir := oInDir;
      Options := oOptions;
    end;
  Result := TntOpenDialog1.Execute;
end;

function TForm1.DoSave(sTitle, sFilter, sExtension: string; Desktop: Boolean): boolean;
begin
  with TntSaveDialog1 do
    begin
      Title := sTitle;
      Filter := sFilter;
      FileName := '';
      DefaultExt := sExtension;

      if Desktop then
        InitialDir := GetRegStringValue('Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders', 'Desktop', HKEY_CURRENT_USER)
      else
        InitialDir := CS.ListDir;

    end;
  Result := TntSaveDialog1.Execute;
end;

procedure TForm1.DoTop;
begin
  if CS.istop = false then
    FormStyle := fsNormal
  else
    FormStyle := fsStayOnTop;

  Form1.Update;
end;

procedure TForm1.Scroll;
begin
  with TntStringGrid1 do
    begin
      if (TopRow > TrackIndex) and (TopRow <> 0) then
        TopRow := TrackIndex;

      if ((TopRow + VisibleRowCount - 1) < TrackIndex) and
         (TopRow < (TrackIndex - VisibleRowCount + 1)) then
        TopRow := TrackIndex - VisibleRowCount + 1;
    end;
end;

procedure TForm1.SavedListsOnClick(Sender: TObject);
begin
  with Sender as TTntMenuItem do
    begin
      PDB.ImportPL(SavedLists[Tag - 300].FileName);
      PDB.LoadDBList;
    end;
end;

procedure TForm1.GenerateSavedLists(PopUp: TMenuItem; Path: WideString; var SL: TSavedLists);
var
  i: integer;
  NewItem: TTntMenuItem;
  Rec: TSearchRecW;
  NewLength: Word;
begin

  //Remove auto generated items
  for i := 1 to PopUp.Count - 1 do
    PopUp.Delete(1);

  //Clear array. We will generate a new one soon.
  SetLength(SL, 0);

  //We must have a trailing backslash
  Path := WideIncludeTrailingBackslash(Path);

  if WideFindFirst(Path + '*.pls', faAnyFile - faDirectory, Rec) = 0 then
    try
      repeat
        NewLength := Length(SL) + 1;

        SetLength(SL, NewLength);
        SL[NewLength - 1].FileName := Path + Rec.Name;
        SL[NewLength - 1].MenuTag := NewLength + 299;

        NewItem := TTntMenuItem.Create(PopUp);
        PopUp.Add(NewItem);

        With NewItem do
          begin
            Tag := NewLength + 299;
            Caption := Rec.Name;
            OnClick := SavedListsOnClick;
          end;

      until
        WideFindNext(Rec) <> 0;
    finally
      WideFindClose(Rec);

      NewItem := TTntMenuItem.Create(PopUp);
      PopUp.Insert(1, NewItem);
      NewItem.Caption := '-';
    end;
end;


end.
